package com.tgh.gles2.c5.pyramid;

import com.tgh.gles2.R;
import com.tgh.utils.GLBatch;
import com.tgh.utils.GLFrame;
import com.tgh.utils.GLFrustum;
import com.tgh.utils.GLGeometryTransform;
import com.tgh.utils.GLMatrixStack;
import com.tgh.utils.GLShaderManager;
import com.tgh.utils.GLShaderManager.ShaderType;
import com.tgh.utils.GLTools;
import com.tgh.utils.Math3D;
import com.tgh.utils.Matrix44f;
import com.tgh.utils.Vector3f;
import com.tgh.utils.Vector4f;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MySurfaceView extends GLSurfaceView {

    public MySurfaceView(Context context) {
        super(context);
        init();
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    
    private void init() {
        setEGLContextClientVersion(2);
        setRenderer(new SceneRenderer());
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
    
    public void up() {
        objectFrame.rotateWorld(-angular, 1, 0, 0);
        requestRender();
    }
    public void down() {
        objectFrame.rotateWorld(angular, 1, 0, 0);
        requestRender();
    }
    public void left() {
        objectFrame.rotateWorld(-angular, 0, 1, 0);
        requestRender();
    }
    public void right() {
        objectFrame.rotateWorld(angular, 0, 1, 0);
        requestRender();
    }
    
    private GLFrame objectFrame;
    private static final float angular=(float) (30*Math.PI/180f);
    private class SceneRenderer implements Renderer{

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            int[] textures = GLTools.genTextures(1);
            currentTexture=textures[0];
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, currentTexture);
            GLTools.loadDrawableToTexture2D(
                    getContext(), R.drawable.pyramid_stone, 
                    GLES20.GL_LINEAR, GLES20.GL_LINEAR, GLES20.GL_CLAMP_TO_EDGE);
            shaderManager = new GLShaderManager();
            shaderManager.initStockShader();
            projectionMatrix=new GLMatrixStack();
            modelViewMatrix=new GLMatrixStack();
            transformPipleline=new GLGeometryTransform();
            cameraFrame=new GLFrame();
            cameraFrame.moveForward(-7);
            objectFrame=new GLFrame();
            initPyramid();
            GLES20.glClearColor(0.7f, 0.7f, 0.7f, 1);
            GLES20.glEnable(GLES20.GL_DEPTH_TEST);
            GLES20.glEnable(GLES20.GL_CULL_FACE);
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            viewFrustum=new GLFrustum(35,(float)width/(float)height,1,500);
            
            projectionMatrix.loadMatrix(viewFrustum.getProjectionMatrix());
            transformPipleline.setMatrixStack(modelViewMatrix, projectionMatrix);
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
            modelViewMatrix.pushMatrix();
                Matrix44f cameraMatrix = cameraFrame.getCameraMatrix(false);
                modelViewMatrix.multMatrix(cameraMatrix);
                
                Vector4f eyeLight=new Vector4f();
                Math3D.transformVector4(eyeLight, lightPos, cameraMatrix);
                modelViewMatrix.multMatrix(objectFrame);
                
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, currentTexture);
                shaderManager.useStockShader(ShaderType.TEXTURE_POINT_LIGHT_DIFF, 
                        transformPipleline.getModelViewMatrix().getArray(),
                        transformPipleline.getProjectionMatrix().getArray(),
                        lightPos.toArray(),
                        white,
                        0
                        );
                        pyramidBatch.draw();
            modelViewMatrix.popMatrix();
        }
        
        private void initPyramid() {
            pyramidBatch = new GLBatch(GLES20.GL_TRIANGLES, 18);
            // Bottom of pyramid
            pyramidBatch.normal(0.0f, -1.0f, 0.0f);
            pyramidBatch.multiTexCoord(0, 0.0f, 0.0f);
            pyramidBatch.vertex(-1.0f, -1.0f, -1.0f);
            
            pyramidBatch.normal(0.0f, -1.0f, 0.0f);
            pyramidBatch.multiTexCoord(0, 1.0f, 0.0f);
            pyramidBatch.vertex(1.0f, -1.0f, -1.0f);
            
            pyramidBatch.normal(0.0f, -1.0f, 0.0f);
            pyramidBatch.multiTexCoord(0, 1.0f, 1.0f);
            pyramidBatch.vertex(1.0f, -1.0f, 1.0f);
            
            pyramidBatch.normal(0.0f, -1.0f, 0.0f);
            pyramidBatch.multiTexCoord(0, 0.0f, 1.0f);
            pyramidBatch.vertex(-1.0f, -1.0f, 1.0f);
            
            pyramidBatch.normal(0.0f, -1.0f, 0.0f);
            pyramidBatch.multiTexCoord(0, 0.0f, 0.0f);
            pyramidBatch.vertex(-1.0f, -1.0f, -1.0f);
            
            pyramidBatch.normal(0.0f, -1.0f, 0.0f);
            pyramidBatch.multiTexCoord(0, 1.0f, 1.0f);
            pyramidBatch.vertex(1.0f, -1.0f, 1.0f);
            
            
            Vector3f vApex =new Vector3f ( 0.0f, 1.0f, 0.0f );
            Vector3f vFrontLeft =new Vector3f ( -1.0f, -1.0f, 1.0f );
            Vector3f vFrontRight =new Vector3f (  1.0f, -1.0f, 1.0f );
            Vector3f vBackLeft = new Vector3f (  -1.0f, -1.0f, -1.0f );
            Vector3f vBackRight = new Vector3f (  1.0f, -1.0f, -1.0f );
            Vector3f n=new Vector3f();
            
            // Front of Pyramid
            Math3D.findNormal(n, vApex, vFrontLeft, vFrontRight);
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.5f, 1.0f);
            pyramidBatch.vertex(vApex);      // Apex
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.0f, 0.0f);
            pyramidBatch.vertex(vFrontLeft);     // Front left corner
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 1.0f, 0.0f);
            pyramidBatch.vertex(vFrontRight);        // Front right corner
            
            
            Math3D.findNormal(n, vApex, vBackLeft, vFrontLeft);
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.5f, 1.0f);
            pyramidBatch.vertex(vApex);      // Apex
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 1.0f, 0.0f);
            pyramidBatch.vertex(vBackLeft);      // Back left corner
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.0f, 0.0f);
            pyramidBatch.vertex(vFrontLeft);     // Front left corner
            
            Math3D.findNormal(n, vApex, vFrontRight, vBackRight);
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.5f, 1.0f);
            pyramidBatch.vertex(vApex);              // Apex
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 1.0f, 0.0f);
            pyramidBatch.vertex(vFrontRight);        // Front right corner
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.0f, 0.0f);
            pyramidBatch.vertex(vBackRight);         // Back right cornder
            
            
            Math3D.findNormal(n, vApex, vBackRight, vBackLeft);
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.5f, 1.0f);
            pyramidBatch.vertex(vApex);      // Apex
            
            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 0.0f, 0.0f);
            pyramidBatch.vertex(vBackRight);     // Back right cornder

            pyramidBatch.normal(n);
            pyramidBatch.multiTexCoord(0, 1.0f, 0.0f);
            pyramidBatch.vertex(vBackLeft);      // Back left corner

            pyramidBatch.end();
        }
        
        private int currentTexture;
        private GLShaderManager shaderManager;
        private GLMatrixStack modelViewMatrix;
        private GLMatrixStack projectionMatrix;
        private GLFrame cameraFrame;
        private GLFrustum viewFrustum;
        private GLGeometryTransform transformPipleline;
        private Vector4f lightPos=new Vector4f(1, 1, 0,1);
        private float[] white=new float[]{1,1,1,1};
        private GLBatch pyramidBatch;
    }
}
